﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;

namespace ZOVNP.Model.Abstracts
{
    /// <summary> Вспомогательный класс для получения сервисов (провайдер всех сервисов) </summary>
    /// <remarks> Поддерживает загрузку сервисов по требованию </remarks>
    [Description("Вспомогательный класс для получения сервисов (провайдер всех сервисов)")]
    public sealed class ServiceLocator : IServiceProvider, IDisposable
    {
        /// <summary> Событие создания сервиса </summary>
        public event EventHandler<ServiceNeededEventArgs> ServiceNeeded;

        /// <summary> Словарь доступных сервисов </summary>
        private readonly Dictionary<System.Type, object> m_services = new Dictionary<System.Type, object>(10);
        /// <summary> Получить сервис указанного типа </summary>
        /// <param name="serviceType"> тип </param>
        /// <returns> сервис или null </returns>
        public object GetService(System.Type serviceType)
        {
            lock (m_services)
            {
                if (m_services.ContainsKey(serviceType))
                {
                    return (m_services[serviceType]);
                }
                else
                {
                    // запрос
                    OnServiceNeeded(serviceType);
                    object result = null;
                    // вторая попытка
                    m_services.TryGetValue(serviceType, out result);
                    return (result);
                }
            }
        }
        /// <summary> Получить сервис указанного типа </summary>
        /// <returns> сервис или null </returns>
        public T GetService<T>() where T : class { return GetService(typeof(T)) as T; }
        /// <summary> Добавить сервис </summary>
        /// <param name="service"> сервис </param>
        public void AddService(object service) { this.AddService(service, service.GetType()); }
        /// <summary> Добавить сервис </summary>
        /// <param name="service"> сервис </param>
        /// <param name="serviceType"> тип сервиса </param>
        /// <remarks> В случае существования указанного типа сервиса происходит его замена </remarks>
        public void AddService(object service, System.Type serviceType)
        {
            lock (m_services)
            {
                // сначала попробуем удалить
                RemoveService(serviceType);
                // сервис может быть null
                m_services.Add(serviceType, service);
            }
        }
        /// <summary> Удалить сервис указанного типа </summary>
        /// <param name="serviceType"> тип </param>
        public void RemoveService(System.Type serviceType)
        {
            lock (m_services)
            {
                if (m_services.ContainsKey(serviceType))
                {
                    object service = m_services[serviceType];
                    m_services.Remove(serviceType);
                    DisposeService(service);
                }
            }
        }
        /// <summary> Очистка ресурсов </summary>
        public void Dispose()
        {
            lock (m_services)
            {
                foreach (object service in m_services.Values)
                    DisposeService(service);

                m_services.Clear();
            }
        }
        /// <summary> Очистка ресурсов сервиса </summary>
        /// <param name="service"> сервис </param>
        private void DisposeService(object service)
        {
            IDisposable tmp = service as IDisposable;
            if (tmp != null)
                tmp.Dispose();
        }
        /// <summary> Генерация события создания сервиса </summary>
        /// <param name="serviceType"> тип сервиса </param>
        private void OnServiceNeeded(Type serviceType)
        {
            if (ServiceNeeded != null)
            {
                ServiceNeededEventArgs e = new ServiceNeededEventArgs(serviceType);
                ServiceNeeded(this, e);
                if (e.Applied)
                    AddService(e.Service, e.Type);
            }
        }
    }
    /// <summary> Аргументы события требования на создание сервиса </summary>
    public class ServiceNeededEventArgs : EventArgs
    {
        /// <summary> Сервис </summary>
        private object m_service;
        /// <summary> Тип </summary>
        private readonly Type m_type;
        /// <summary> Флаг обработки </summary>
        private bool m_applied;

        /// <summary> Конструктор с типом сервиса </summary>
        /// <param name="type"> тип сервиса </param>
        public ServiceNeededEventArgs(Type type)
        {
            m_type = type;
        }
        /// <summary> Получить (установить) запрашиваемый сервис </summary>
        /// <value>  сервис </value>
        public object Service
        {
            get { return (m_service); }
            set
            {
                m_service = value;
                m_applied = true;
            }
        }
        /// <summary> Получить тип запрашиваемого сервиса </summary>
        /// <value> тип сервиса </value>
        public Type Type { get { return (m_type); } }
        /// <summary> Получить признак обработки </summary>
        /// <value> true, если событие обработано, в противном случае - false </value>
        public bool Applied
        {
            get { return (m_applied); }
            set { m_applied = value; }
        }
    }
}
